﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Cors;
using System.Web.Http.Cors;

namespace System.Web.Http.Cors
{
    public class DynamicPolicyProviderFactory : ICorsPolicyProviderFactory
    {
        private readonly HashSet<Regex> _allowed;
        private bool isAllowAll;

        public DynamicPolicyProviderFactory(IEnumerable allowedOrigins)
        {
            var allowedStr = allowedOrigins.Cast<string>();
            _allowed = new HashSet<Regex>();

            if (allowedStr.Contains("*"))
            {
                isAllowAll = true;
                return;
            }

            foreach (string pattern in allowedStr.Select(Regex.Escape)
                .Select(pattern => pattern.Replace("*", "w*")))
            {
                _allowed.Add(new Regex(pattern, RegexOptions.IgnoreCase));
            }

            if (_allowed.Count >= 1)
                return;

            //if nothing is specified, we assume everything is.
            _allowed.Add(new Regex(@"https://\w*", RegexOptions.IgnoreCase));
            _allowed.Add(new Regex(@"http://\w*", RegexOptions.IgnoreCase));
        }

        public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
        {
            var route = request.GetRouteData();
            string controller = null;
            try
            {
                controller = (string)route.Values["controller"];
            }
            catch (Exception)
            {
            }

            var corsRequestContext = request.GetCorsRequestContext();
            var originRequested = corsRequestContext.Origin;
            var policy = GetPolicyForControllerAndOrigin(controller, originRequested);
            return new CustomPolicyProvider(policy);
        }

        private CorsPolicy GetPolicyForControllerAndOrigin(string controller, string originRequested)
        {
            // Do lookup to determine if the controller is allowed for
            // the origin and create CorsPolicy if it is (otherwise return null)

            if (!isAllowAll)
            {
                if (_allowed.All(a => !a.Match(originRequested).Success))
                    return null;
            }

            var policy = new CorsPolicy();
            policy.Origins.Add(originRequested);
            policy.AllowAnyHeader = true;
            //policy.Headers.Add("Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
            policy.SupportsCredentials = true;
            policy.Methods.Add("GET");
            policy.Methods.Add("POST");
            policy.Methods.Add("PUT");
            policy.Methods.Add("DELETE");
            policy.Methods.Add("OPTIONS");
            return policy;
        }
    }


    public class CustomPolicyProvider : ICorsPolicyProvider
    {
        private readonly CorsPolicy _policy;

        public CustomPolicyProvider(CorsPolicy policy)
        {
            this._policy = policy;
        }

        public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return Task.FromResult(this._policy);
        }
    }
}